home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / libg_261.zip / libg_261 / libg++ / etc / trie-gen / compact.cc < prev    next >
C/C++ Source or Header  |  1994-02-06  |  13KB  |  402 lines

  1. /* Compact a sparse 2-D matrix.  Uses the Tarjan and Yao algorithm
  2.    taken from the article ``Storing a Sparse Table'' in CACM, 1979.
  3.  
  4.    Copyright (C) 1989 Free Software Foundation, Inc.
  5.    written by Douglas C. Schmidt (schmidt@ics.uci.edu)
  6.    
  7.    This file is part of GNU TRIE-GEN.
  8.    
  9.    GNU TRIE-GEN is free software; you can redistribute it and/or modify
  10.    it under the terms of the GNU General Public License as published by
  11.    the Free Software Foundation; either version 1, or (at your option)
  12.    any later version.
  13.    
  14.    GNU TRIE-GEN is distributed in the hope that it will be useful,
  15.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.    GNU General Public License for more details.
  18.    
  19.    You should have received a copy of the GNU General Public License
  20.    along with GNU trie-gen; see the file COPYING.  If not, write to
  21.    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  22.  
  23. #include <stdio.h>
  24. #include <builtin.h>
  25. #include <limits.h>
  26. #include <new.h>
  27. #include <std.h>
  28. #include <assert.h>
  29. #include "renew.h"
  30. #include "compact.h"
  31.  
  32. /* Essentially provides the functionality of `calloc.' */
  33.  
  34. static inline void *
  35. operator new (size_t elem_size, int size)
  36. {
  37.   void *temp = new char[elem_size * size];
  38.   memset (temp, 0, elem_size * size);
  39.   return temp;
  40. }
  41.  
  42. /* Essentially combines the functionality of `realloc' and `calloc'. */
  43.  
  44. static inline void *
  45. operator new (size_t elem_size, void *old_ptr, int old_size, int new_size)
  46.   old_ptr = new (old_ptr, new_size * elem_size) char;
  47.   memset ((char*)old_ptr + old_size * elem_size, 0,
  48.       (new_size - old_size) * elem_size);
  49.   return old_ptr;
  50. }
  51.  
  52. /* Initializes the internal form in the case that the user passes
  53.    in a pointer to an already existing 2-dimensional matrix.  Note
  54.    that by declaring the matrix to be a 1-dimensional array we
  55.    can perform the col and row offset calculations ourselves and
  56.    handle matrices with fixed, but arbitrary-sized columns and rows. */
  57.  
  58. Compact_Matrix::Compact_Matrix (ITEM_TYPE *mat, int rows, int cols):
  59.      matrix (mat), max_rows (rows), total_cols (cols), current_rows (rows)
  60. {
  61.   init (rows);
  62.  
  63.   for (int i = 0; i < max_rows; i++)
  64.     for (int j = 0; j < total_cols; j++)
  65.       if (matrix[i * total_cols + j] != 0)
  66.         {
  67.           ITEM_TYPE value = matrix[i * total_cols + j];
  68.           total_entries++;
  69.           row_vec[i].count++;
  70.           row_vec[i].col_list = new Column_Node (row_vec[i].col_list, j, value);
  71.         }
  72. }
  73.  
  74. /* Initializer for the case where we don't have a previously created
  75.    matrix to play with.  DEFAULT_ROWS represents the best first
  76.    approximation as to the number of rows in the matrix.  However, this
  77.    buffer is resized as needed, so the algorithm isn't overly penalized
  78.    for a bad first guess. */
  79.  
  80. Compact_Matrix::Compact_Matrix (int default_rows): max_rows (default_rows)
  81. {
  82.   current_rows = 0;
  83.   total_cols = 0;
  84.   matrix = 0;
  85.  
  86.   init (default_rows);
  87. }
  88.  
  89. void Compact_Matrix::init(int rows)
  90. {
  91.   max_col_count = 0;
  92.   total_entries = 0;
  93.   compressed_len = -1;
  94.   row_offsets = 0;
  95.   checks = 0;
  96.   bucket_vec = 0;
  97.   values = 0;
  98.  
  99.   row_vec = rows ? new Row_Node[rows] : 0;
  100. }
  101.  
  102. /* Returns the `matrix[i][j]' item in the sparse 2-dimensional matrix.
  103.    Note that if the matrix is very sparse the number of items in each
  104.    COL_LIST will be very short, hence linear search is not too inefficent. */
  105.  
  106. ITEM_TYPE 
  107. Compact_Matrix::operator () (int i, int j)
  108. {
  109.   assert (i >= 0 && i < current_rows && j >= 0);
  110.   
  111.   for (Column_Node *col_list = row_vec[i].col_list; col_list; col_list = col_list->next)
  112.     if (col_list->index == j)
  113.       return col_list->value;
  114.  
  115.   return 0;
  116. }
  117.  
  118. /* Sets `matrix[i][j]' to VALUE.  ROW_VEC is dynamically resized, if
  119.    necessary.  At the moment the new entry is simply pushed onto the
  120.    linked list of COL_LIST entries.  If there aren't many elements then
  121.    this will not be too inefficient for later retrieval. */
  122.  
  123. void
  124. Compact_Matrix::operator () (int i, int j, ITEM_TYPE value)
  125. {
  126.   if (i >= max_rows)
  127.     resize (((max_rows * 2) > (i + 1)) ? (max_rows * 2) : (i + 1));
  128.   row_vec[i].col_list = new Column_Node (row_vec[i].col_list, j, value);
  129.   total_entries++;
  130.   row_vec[i].count++;
  131.   if (current_rows < (i + 1))
  132.     current_rows = i + 1;
  133. }
  134.  
  135. /* Enlarges the ROW_VEC from CURR_ROWS to NEW_SIZE. */
  136.  
  137. void 
  138. Compact_Matrix::resize (int new_size)
  139. {
  140.   Row_Node *temp = new Row_Node[max_rows = new_size];
  141.   
  142.   memcpy ((void *) temp, (void *) row_vec, current_rows * sizeof *row_vec);
  143.   delete row_vec;
  144.   row_vec = temp;
  145. }
  146.  
  147. /* Calls the functions that compact the table and generate the resulting
  148.    lookup scheme. */
  149.  
  150. void
  151. Compact_Matrix::output (void)
  152. {
  153.   bucket_sort ();
  154.   first_fit_decreasing ();
  155.   output_arrays ();
  156.   output_lookup ();
  157. }
  158.  
  159. /* Performs a bucket sort so that all rows with the same number of non-null
  160.    entries are treated as part of the same equivalence class.  This
  161.    operation is very fast! */
  162.  
  163. void 
  164. Compact_Matrix::bucket_sort (void)
  165. {
  166.   for (int i = 0; i < current_rows; i++)
  167.     {
  168.       if (max_col_count < row_vec[i].count)
  169.         max_col_count = row_vec[i].count;
  170.     }
  171.   
  172.   bucket_vec = new (max_col_count + 1) Column_Node *;
  173.  
  174.   for (i = 0; i < current_rows; i++)
  175.     bucket_vec[row_vec[i].count] = new Column_Node (bucket_vec[row_vec[i].count], i, 0);
  176. }
  177.  
  178. /* Useful macros to clarify subsequent code.  They should probably be made
  179.    into member functions... */
  180. #define STARTING_ROW_OFFSET(X) (row_offsets[(X)])
  181. #define LARGEST_COL_VALUE(X) (row_vec[(X)].col_list->index)
  182. #define COL_LIST(X) (row_vec[(X)].col_list)
  183. #define COL_COUNT(X) (row_vec[(X)].count)
  184. #define COL_INDEX(X) ((X)->index)
  185. #define ROW_INDEX(X) ((X)->index)
  186.  
  187. const int     MAX_ASCII_RANGE    = 128;
  188.  
  189. /* Performs sparse 2-dimensional array compaction suitable for use with the
  190.    `double-offset indexing' (used by Bison and FLEX to compact the size of
  191.    the sparse LR parsing tables and DFA's).  This function implements the
  192.    `first fit decreasing' heuristic described in Tarjan and Yao's paper 
  193.    ``Storing a Sparse Table'' in CACM, 1979. */
  194.  
  195. void 
  196. Compact_Matrix::first_fit_decreasing (void)
  197. {
  198.   /* Bit-vector and counter that records if a row/col location is already set. */
  199.   int    current_max = current_rows
  200.     + ((total_cols > MAX_ASCII_RANGE) ? total_cols : MAX_ASCII_RANGE);
  201.   char *already_assigned = (char *) malloc (current_max);
  202.   
  203.   memset (already_assigned, 0, current_max);
  204.   row_offsets = new (current_rows) int;
  205.   values      = new (current_max) ITEM_TYPE;
  206.   checks      = new (current_max) int;
  207.  
  208.   for (int row_index = max_col_count; row_index >= 0; row_index--)
  209.     if (bucket_vec[row_index])
  210.  
  211.       /* Process every row in the `equivalence class' of rows containing the
  212.          same number of non-null column entries. */
  213.  
  214.       for (Column_Node *rows_list = bucket_vec[row_index]; rows_list; rows_list = rows_list->next)
  215.         {
  216.           int row        = ROW_INDEX (rows_list);
  217.           int row_offset = STARTING_ROW_OFFSET (row);
  218.           
  219.           /* Process every column index in the current row. */
  220.  
  221.           for (Column_Node *col_list = COL_LIST (row); ;)
  222.             {
  223.               int col = COL_INDEX (col_list);
  224.  
  225.               /* If we exceed the boundaries it's time to resize various buffers. */
  226.               if (row_offset + col >= current_max)
  227.                 {
  228.                   int   new_size = ((current_max > (row_offset + col)) ? current_max : (row_offset + col)) * 2;
  229.                   already_assigned =
  230.               (char *) realloc (already_assigned, new_size);
  231.  
  232.           memset (already_assigned + current_max, 0,
  233.               new_size - current_max);
  234.                   values = new (values, current_max, new_size) int;
  235.                   checks = new (checks, current_max, new_size) int;
  236.                   current_max *= 2;
  237.                 }
  238.               if (already_assigned[row_offset + col])
  239.                 {
  240.                   /* Efficiency hack to skip over obvious collisions. */
  241.  
  242.                   while (++row_offset + col < current_max && already_assigned[row_offset + col]) 
  243.                     ;
  244.  
  245.                   /* Reset col_list and begin again (with new row offset). */
  246.                   col_list = COL_LIST (row);
  247.                 }
  248.               else if ((col_list = col_list->next) == 0)
  249.                 break;
  250.             }
  251.  
  252.           /* No more collisions exist.  Record the positions for the next round. */
  253.  
  254.           for (col_list = COL_LIST (row); col_list; col_list = col_list->next)
  255.             {
  256.               int offset = row_offset + COL_INDEX (col_list);
  257.  
  258.               already_assigned[offset] = 1;
  259.               if (compressed_len < offset)
  260.         compressed_len = offset;
  261.               values[offset] = col_list->value;
  262.               checks[offset] = row;
  263.             }
  264.  
  265.           /* Need to reset this once all is said and done! */
  266.           STARTING_ROW_OFFSET (row) = row_offset;
  267.         }
  268.   free(already_assigned);
  269. }
  270.  
  271. /* Generates the three arrays that comprise the `double-offset index'
  272.    scheme.  This is a bit messy, since I'm trying to neatly format
  273.    the generated tables and also determine the smallest (in bytes)
  274.    type declarations necessary to represent the elements of the tables. */
  275.  
  276. void 
  277. Compact_Matrix::output_arrays ()
  278. {
  279.   const int COL_WIDTH = 12;
  280.         int max_number = 0;
  281.         int count;
  282.       
  283.   printf ("#if !defined(__STDC__) && !defined(__cplusplus)\n");
  284.   printf ("#define const\n");
  285.   printf ("#define signed\n");
  286.   printf ("#endif\n\n");
  287.   printf ("#define YY_LAST %d\n", compressed_len);
  288.       
  289.   for (int i = 0; i < current_rows; i++)
  290.     {
  291.       if (max_number < row_offsets[i])
  292.         max_number = row_offsets[i];
  293.     }
  294.  
  295.   count = max_number;
  296.   for (int field_width = 1; (count /= 10) > 0; field_width++)
  297.     ;
  298.  
  299.   printf ("\nstatic const unsigned %s yy_rows[%d] = \n{\n  ",
  300.           max_number < UCHAR_MAX ? "char" : max_number < USHRT_MAX ? "short" : "int",
  301.           current_rows);
  302.       
  303.   for (i = 0; i < current_rows; i++)
  304.     printf ("%*d,%s", field_width, row_offsets[i], (i + 1) % COL_WIDTH ? " " : "\n  ");
  305.       
  306.   max_number = 0;
  307.  
  308.   for (i = 0; i < compressed_len + 1; i++)
  309.     {
  310.       if (max_number < checks[i])
  311.         max_number = checks[i];
  312.     }
  313.  
  314.   count = max_number;
  315.   for (field_width = 1; (count /= 10) > 0; field_width++)
  316.     ;
  317.  
  318.   printf ("\n};\n\nstatic const unsigned %s yy_check[%d] = \n{\n  ",
  319.           max_number < UCHAR_MAX ? "char" : max_number < USHRT_MAX ? "short" : "int",
  320.           compressed_len + 1);
  321.       
  322.   for (i = 0; i < compressed_len + 1; i++)
  323.     printf ("%*d,%s", field_width, checks[i], (i + 1) % COL_WIDTH ? " " : "\n  ");
  324.       
  325.   max_number = 0;
  326.  
  327.   for (i = 0; i < compressed_len + 1; i++)
  328. #ifdef __GNUC__
  329.     max_number >?= abs (values[i]);
  330. #else
  331.     {
  332.       int tmp = abs (values[i]);
  333.       if (max_number < tmp)
  334.         max_number = tmp;
  335.     }
  336. #endif
  337.  
  338.   count = max_number;
  339.   for (field_width = 2; (count /= 10) > 0; field_width++)
  340.     ;
  341.  
  342.   printf ("\n};\n\nstatic const %s yy_next[%d] = \n{\n  ",
  343.           max_number < SCHAR_MAX ? (CHAR_MIN == 0 ? "signed char" : "char")
  344.       : max_number < SHRT_MAX ? "short" : "int",
  345.           compressed_len + 1);
  346.       
  347.   for (i = 0; i <= compressed_len; i++)
  348.     printf ("%*d,%s", field_width, values[i], (i + 1) % COL_WIDTH ? " " : "\n  ");
  349.       
  350.   printf ("\n};\n\n");
  351. }
  352.  
  353. /* Generates the `double-offset index' function, that provides the
  354.    value stored in the location referenced by parameters ROW and COL. */
  355.  
  356. void 
  357. Compact_Matrix::output_lookup (void)
  358. {
  359.   printf ("static inline int\nnext_state "
  360.           "(int row, int col)\n{\n  int state_index = yy_rows[row] + col;\n\n"
  361.           "  if (state_index > YY_LAST || yy_check[state_index] != row)\n    "
  362.           "return 0;\n  else\n    return yy_next[state_index];\n}\n");
  363. }
  364.  
  365. /* Useful debugging routine. */
  366.  
  367. void
  368. Compact_Matrix::dump_entries (void)
  369. {
  370.   for (int i = 0; i < current_rows; i++)
  371.     if (row_vec[i].col_list)
  372.       {
  373.         Column_Node *temp = row_vec[i].col_list;
  374.       
  375.         fprintf (stderr, "row %d's count = %d, cols = ", i, row_vec[i].count);
  376.  
  377.         do { fprintf (stderr, "%d ", temp->index); } while (temp = temp->next);
  378.  
  379.         putc ('\n', stderr);
  380.       }
  381. }
  382.  
  383. /* Useful debugging routine. */
  384.  
  385. void 
  386. Compact_Matrix::dump_bucket (void)
  387. {
  388.   for (int i = 0; i < total_entries; i++)
  389.     if (bucket_vec[i])
  390.       {
  391.         Column_Node *temp = bucket_vec[i];
  392.         
  393.         fprintf (stderr, "bucket %d = ", i);
  394.  
  395.         do { fprintf (stderr, "%d ", temp->index); } while (temp = temp->next);
  396.  
  397.         putc ('\n', stderr);
  398.       }
  399. }
  400.  
  401.